#ifndef     __RESP_H
#define     __RESP_H

#include <unordered_map>
#include "../utils/byte_buf.h"
#include "./proto.h"
#include "../utils/codec.h"
#include "../rt/time_future.h"

namespace conn {

class RespMap : private utils::NoCopyable {

public:
    // run on ui
    void handle_res(utils::ByteBuf&& buf) {
        uint8_t type = buf[0];
        uint8_t seq = buf[1];
        uint16_t id = (type << 8) + seq;

        auto iter = m_res_map.find(id);
        if (iter == m_res_map.end()) {
            return;
        }
        auto notify = iter->second.lock();
        m_res_map.erase(id);
        if (notify) {
            notify->complete( std::move(buf) );
        }
    }

    rt::TimeoutFuture2<utils::ByteBuf> resp(uint8_t type, uint8_t seq, int timeout = 2000) {
        rt::TimeoutFuture2<utils::ByteBuf> fut(timeout);
        uint16_t id = (type << 8) + seq;
        m_res_map.insert(std::pair( id, fut.weak() ));
        return fut;
    }

    rt::TimeoutFuture2<utils::ByteBuf> ack(uint8_t seq, int timeout = 2000) {
        auto fut = resp(proto::type::ACK, seq, timeout);
        return fut;
    }

    rt::TimeoutFuture2<utils::ByteBuf> pong(uint8_t seq, int timeout = 2000) {
        auto fut = resp(proto::type::PONG, seq, timeout);
        return fut;
    }

private:
    std::unordered_map<uint16_t, std::weak_ptr<rt::Notify2<utils::ByteBuf>>> m_res_map;
};

template <typename... Ts>
inline utils::ByteBuf make_proto(uint8_t type, uint8_t seq, const Ts&... ts) {
    uint8_t data_size = (uint8_t) utils::codec::value_size(ts...);
    utils::ByteBuf buf( data_size + 7 );
    utils::codec::encode( buf.buf(), proto::HEAD0, proto::HEAD1, data_size, type, seq, ts... );
    uint8_t s = 0;
    for (uint8_t i = 0; i < (2 + data_size); i ++) {
        s ^= buf[i + 3];
    }
    buf[ 5 + data_size ] = s;
    buf[ 6 + data_size ] = proto::END;
    return buf;
}

class Resp {

public:
    Resp(utils::ByteBuf&& buf) : m_buf( std::move(buf) ) {}

    uint8_t type() { return m_buf[0]; }
    uint8_t seq() { return m_buf[1]; }
    uint8_t cmd() { return m_buf[2]; }
    uint8_t ec() { return m_buf[3]; }

    template <typename... Ts>
    void parse(uint8_t r_cmd, Ts&... ts) {
        if (m_buf.len() < 6) {
            throw std::runtime_error("invalid resp");   
        }
        
        if (cmd() != r_cmd) {
            throw std::runtime_error("invalid cmd result");
        }

        if (ec() != 0) {
            throw std::runtime_error("exce fail");
        }

        bool ret = utils::codec::decode(m_buf.buf() + 4, m_buf.len() - 6, ts...);
        if (!ret) {
            throw std::runtime_error("parse fail");
        }
    }

private:
    utils::ByteBuf m_buf;
};

}


#endif
